/* -*-  Mode:C; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
 * Copyright (c) 1999, 2000 Massachusetts Institute of Technology.
 *
 * This software is being provided by the copyright holders under the GNU
 * General Public License, either version 2 or, at your discretion, any later
 * version. (See the file `LICENSE' in the source distribution.)
 *
 * This software is provided "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The entire risk as
 * to the quality and performance of the program is with you. See the GNU
 * General Public License for more details.
 *
 * The name and trademarks of copyright holders may NOT be used in 
 * advertising or publicity pertaining to the software without specific, 
 * written prior permission. Title to copyright in this software and any 
 * associated documentation will at all times remain with copyright 
 * holders. See the file CMAUTHORS which should have accompanied this software 
 * for a list of all copyright holders. 
 * 
 * Particular files in this distribution may be derived from previously
 * copyrighted software. This copyright applies only to those changes made by
 * the copyright holders listed in the CMAUTHORS file.
 */
/*
 * An implementation of the Congestion Manager (CM) for Linux.
 * This file translates socket calls into the CM API.
 *
 * Huge portions of this file are stolen from raw sockets (raw.c).
 *
 */
#include <linux/config.h> 
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/mroute.h>
#include <linux/poll.h>
#include <linux/smp_lock.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/icmp.h>
#include <net/udp.h>
#include <net/cm.h>
#include <net/cm_debug.h>
#include <net/checksum.h>
extern struct socket *sockfd_lookup(int fd, int *err);
extern __inline__ void sockfd_put(struct socket *sock)
{
        fput(sock->file);
}
extern int *cm_log_data;


#ifdef CONFIG_IP_MROUTE
struct sock *mroute_socket=NULL;
#endif

#ifdef CMDEMO
static unsigned int cm_get_time_rdtsc(void)
{
        unsigned int x;
        __asm__ __volatile__ ( "rdtsc" : "=A" (x) );
        return x;
}
#endif


struct sock *cm_v4_htable[CMV4_HTABLE_SIZE];

static void cm_v4_hash(struct sock *sk)
{
	struct sock **skp;
	int num = sk->num;
	/* printk("hashing %d\n",num); */
	num &= (CMV4_HTABLE_SIZE - 1);
	skp = &cm_v4_htable[num];
	SOCKHASH_LOCK();
	sk->next = *skp;
	*skp = sk;
	sk->hashent = num;
	SOCKHASH_UNLOCK();
}

static void cm_v4_unhash(struct sock *sk)
{
	struct sock **skp;
	int num = sk->num;

	num &= (CMV4_HTABLE_SIZE - 1);
	skp = &cm_v4_htable[num];

	SOCKHASH_LOCK();
	while(*skp != NULL) {
		if(*skp == sk) {
			*skp = sk->next;
			break;
		}
		skp = &((*skp)->next);
	}
	SOCKHASH_UNLOCK();
}

static void cm_v4_rehash(struct sock *sk)
{
	struct sock **skp;
	int num = sk->num;
	int oldnum = sk->hashent;

	num &= (CMV4_HTABLE_SIZE - 1);
	skp = &cm_v4_htable[oldnum];

	SOCKHASH_LOCK();
	while(*skp != NULL) {
		if(*skp == sk) {
			*skp = sk->next;
			break;
		}
		skp = &((*skp)->next);
	}
	sk->next = cm_v4_htable[num];
	cm_v4_htable[num] = sk;
	sk->hashent = num;
	SOCKHASH_UNLOCK();
}

/*finds control socket from the data socket*/
/*note only port # of data socket (arguement is used*/
struct sock *cm_v4_findsk(struct sock *sk)
{
	struct sock **skp;
	int num = sk->num;
	/* printk("finding in hashtable %d\n",num); */
	num &= (CMV4_HTABLE_SIZE - 1);
	skp = &cm_v4_htable[num];
//no need to lock table, just looking in the table	
	while(*skp != NULL) {
		if((*skp)->num == (sk->num)) {
			/* printk("CONTOL SOCKET FOUND: cm_v4_findsk\n"); */
			return (*skp);
		}
		skp = &((*skp)->next);
	}
	/* printk("not found control socket: cm_v4_findsk\n"); */
	return NULL;        //no control socket attached to port sk->num

}

/* Grumble... icmp and ip_input want to get at this... */
struct sock *cm_v4_lookup(struct sock *sk, unsigned short num,
			   unsigned long raddr, unsigned long laddr, int dif)
{
	struct sock *s = sk;

	SOCKHASH_LOCK();
	for(s = sk; s; s = s->next) {
		if((s->num == num) 				&&
		   !(s->dead && (s->state == TCP_CLOSE))	&&
		   !(s->daddr && s->daddr != raddr) 		&&
		   !(s->rcv_saddr && s->rcv_saddr != laddr)	&&
		   !(s->bound_dev_if && s->bound_dev_if != dif))
			break; /* gotcha */
	}
	SOCKHASH_UNLOCK();
	return s;
}

static int cm_rcv_skb(struct sock * sk, struct sk_buff * skb)
{
	/* Charge it to the socket. */
	
	if (sock_queue_rcv_skb(sk,skb)<0)
	{
		ip_statistics.IpInDiscards++;
		kfree_skb(skb);
		return -1;
	}

	ip_statistics.IpInDelivers++;
	return 0;
}

/*
 * There's no raw_rcv()-equivalent here; cm_rcv() in cm_rcv.c does this.
 */
struct cmfakehdr 
{
	struct  iovec *iov;
	u32	saddr;
};

/*
 *	Send a CM IP packet.
 */

/*
 *	Callback support is trivial for SOCK_CM
 */
  
static int cm_getfrag(const void *p, char *to, unsigned int offset, unsigned int fraglen)
{
	struct cmfakehdr *rfh = (struct cmfakehdr *) p;
	return memcpy_fromiovecend(to, rfh->iov, offset, fraglen);
}

/*
 *	IPPROTO_CM needs extra work.
 */
 
static int cm_getcmfrag(const void *p, char *to, unsigned int offset, unsigned int fraglen)
{
	struct cmfakehdr *rfh = (struct cmfakehdr *) p;

	if (memcpy_fromiovecend(to, rfh->iov, offset, fraglen))
		return -EFAULT;

	if (offset==0) {
		struct iphdr *iph = (struct iphdr *)to;
		if (!iph->saddr)
			iph->saddr = rfh->saddr;
		iph->check=0;
		iph->tot_len=htons(fraglen);	/* This is right as you can't frag
						   CM packets */
		/*
	 	 *	Deliberate breach of modularity to keep 
	 	 *	ip_build_xmit clean (well less messy).
		 */
		if (!iph->id)
			iph->id = htons(ip_id_count++);
		iph->check=ip_fast_csum((unsigned char *)iph, iph->ihl);
	}
	return 0;
}

static int cm_sendmsg(struct sock *sk, struct msghdr *msg, int len)
{
	struct ipcm_cookie ipc;
	struct cmfakehdr rfh;
	struct rtable *rt = NULL;
	int free = 0;
	u32 daddr;
	u8  tos;
	int err;

	/* This check is ONLY to check for arithmetic overflow
	   on integer(!) len. Not more! Real check will be made
	   in ip_build_xmit --ANK

	   BTW socket.c -> af_*.c -> ... make multiple
	   invalid conversions size_t -> int. We MUST repair it f.e.
	   by replacing all of them with size_t and revise all
	   the places sort of len += sizeof(struct iphdr)
	   If len was ULONG_MAX-10 it would be cathastrophe  --ANK
	 */

	if (len < 0 || len > 0xFFFF)
		return -EMSGSIZE;

	/*
	 *	Check the flags.
	 */

	if (msg->msg_flags & MSG_OOB)		/* Mirror BSD error message compatibility */
		return -EOPNOTSUPP;
			 
	if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
		return(-EINVAL);

	/*
	 *	Get and verify the address. 
	 */

	if (msg->msg_namelen) {
		struct sockaddr_in *usin = (struct sockaddr_in*)msg->msg_name;
		if (msg->msg_namelen < sizeof(*usin))
			return(-EINVAL);
		if (usin->sin_family != AF_INET) {
			static int complained;
			if (!complained++)
				printk(KERN_INFO "%s forgot to set AF_INET in cm sendmsg. Fix it!\n", current->comm);
			if (usin->sin_family)
				return -EINVAL;
		}
		daddr = usin->sin_addr.s_addr;
		/* ANK: I did not forget to get protocol from port field.
		 * I just do not know, who uses this weirdness.
		 * IP_HDRINCL is much more convenient.
		 */
	} else {
		if (sk->state != TCP_ESTABLISHED) 
			return(-EINVAL);
		daddr = sk->daddr;
	}

	ipc.addr = sk->saddr;
	ipc.opt = NULL;
	ipc.oif = sk->bound_dev_if;

	if (msg->msg_controllen) {
		int tmp = ip_cmsg_send(msg, &ipc);
		if (tmp)
			return tmp;
		if (ipc.opt)
			free=1;
	}

	rfh.saddr = ipc.addr;
	ipc.addr = daddr;

	if (!ipc.opt)
		ipc.opt = sk->opt;

	if (ipc.opt) {
		err = -EINVAL;
		/* Linux does not mangle headers on cm sockets,
		 * so that IP options + IP_HDRINCL is non-sense.
		 */
		if (sk->ip_hdrincl)
			goto done;
		if (ipc.opt->srr) {
			if (!daddr)
				goto done;
			daddr = ipc.opt->faddr;
		}
	}
	tos = RT_TOS(sk->ip_tos) | sk->localroute;
	if (msg->msg_flags&MSG_DONTROUTE)
		tos |= RTO_ONLINK;

	if (MULTICAST(daddr)) {
		if (!ipc.oif)
			ipc.oif = sk->ip_mc_index;
		if (!rfh.saddr)
			rfh.saddr = sk->ip_mc_addr;
	}

	err = ip_route_output(&rt, daddr, rfh.saddr, tos, ipc.oif);

	if (err)
		goto done;

	err = -EACCES;
	if (rt->rt_flags&RTCF_BROADCAST && !sk->broadcast)
		goto done;

	rfh.iov = msg->msg_iov;
	rfh.saddr = rt->rt_src;
	if (!ipc.addr)
		ipc.addr = rt->rt_dst;
	err=ip_build_xmit(sk, sk->ip_hdrincl ? cm_getcmfrag : cm_getfrag,
			  &rfh, len, &ipc, rt, msg->msg_flags);

done:
	if (free)
		kfree(ipc.opt);
	ip_rt_put(rt);

	return err<0 ? err : len;
}

/*
 * cm_close() taken up in cm_api.c
 */
static void cm_closer(struct sock *sk, long timeout)
{
	/* printk("Entering cm_closer\n"); */
	/* First of all clean up the CM stuff. */
	if (SK_CM_FLOW(sk)) cm_close(SK_CM_FLOW(sk));

	/* Observation: when cm_closer is called, processes have
	   no access to socket anymore. But net still has.
	   Step one, detach it from networking:

	   A. Remove from hash tables.
	 */
	sk->state = TCP_CLOSE;
	cm_v4_unhash(sk);
        /*
	   B. Raw sockets may have direct kernel refereneces. Kill them.
	 */
	ip_ra_control(sk, 0, NULL);

	/* In this point socket cannot receive new packets anymore */


	/* But we still have packets pending on receive
	   queue and probably, our own packets waiting in device queues.
	   sock_destroy will drain receive queue, but transmitted
	   packets will delay socket destruction.
	   Set sk->dead=1 in order to prevent wakeups, when these
	   packet will be freed.
	 */
	sk->dead=1;
	destroy_sock(sk);

	/* That's all. No races here. */
}

/* This gets rid of all the nasties in af_inet. -DaveM */
static int cm_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
	struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
	int chk_addr_ret;

	/* printk("Entering cm_bind\n"); */
	if((sk->state != TCP_CLOSE) || (addr_len < sizeof(struct sockaddr_in)))
		return -EINVAL;
	chk_addr_ret = inet_addr_type(addr->sin_addr.s_addr);
	if(addr->sin_addr.s_addr != 0 && chk_addr_ret != RTN_LOCAL &&
	   chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST) {
#ifdef CONFIG_IP_TRANSPARENT_PROXY
		/* Superuser may bind to any address to allow transparent proxying. */
		if(chk_addr_ret != RTN_UNICAST || !capable(CAP_NET_ADMIN))
#endif
			return -EADDRNOTAVAIL;
	}
	sk->rcv_saddr = sk->saddr = addr->sin_addr.s_addr;
	if(chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
		sk->saddr = 0;  /* Use device */
	dst_release(xchg(&sk->dst_cache, NULL));
	return 0;
}

int cm_current = 0;
int cm_ioctl(struct sock *sk, int cmd, unsigned long arg)
{

	/* printk("Entering cm_ioctl %p %x\n", sk, cmd);*/
  
	switch (cmd) {
	case SIOCGIFMTU: {
		/* hijacked to return contents of cm_log */
		if (sk->dport == 0) {
			if (copy_to_user((int *)arg, &cm_log_data,
				CM_LOG_SIZE)) return -EFAULT;
			return CM_LOG_SIZE;
		}
		if (copy_to_user((void *)arg, &(sk->dst_cache->pmtu),
				 sizeof(sk->dst_cache->pmtu)))
			return -EFAULT;
		return 0;
	}
	case SIOCCMNOTIFY: {
		int nsent;
		if (copy_from_user(&nsent, (void *)arg, sizeof(nsent)))
			return -EFAULT;
		/* printk("NOTIFY: %d\n", nsent); */
		cm_notify(SK_CM_FLOW(sk), nsent);
		return 0;
	}
	case SIOCCMREQUEST: {
		int err = cm_request(SK_CM_FLOW(sk));
		if (err == -1) return -EBUSY;
		return 0; 
	}
	case SIOCCMBULKREQUEST: {
	      	int count;
	      	int fd;
	      	int err;
		int i = 0;
	      	struct socket *sock;
	      	struct sock *lsk;
		cm_flow_t *flow;
	      	if (copy_from_user(&count, (void *)arg, sizeof(count)))
			return -EFAULT;
              	/* printk("bulkrequest: nfds %d\n", count); */
	      	for (i = 1; i <= count; i++) {
	      		if (copy_from_user(&fd, &(((int *)arg)[i]), sizeof(fd)))
				return -EFAULT;
                	/* printk("bulkrequest: pid %d fd %d\n", current->pid, fd); */
			lock_kernel();
			sock = sockfd_lookup(fd, &err);
			if (!sock) goto br_done;
			lsk = sock->sk;
			flow = SK_CM_FLOW(lsk);
			if (flow == NULL) {
				/* printk("bulkrequest: no flow\n"); */
				goto br_done;
				}
			err = cm_request(SK_CM_FLOW(lsk));
			sockfd_put(sock);
br_done:
			unlock_kernel();
			}
	      return 0;
	}
	case SIOCCMBULKNOTIFY: {
	      	int count;
	      	int fd;
	      	int i;
	      	int err;
		int nsent;
	      	struct socket *sock;
	      	struct sock *lsk;
	      	if (copy_from_user(&count, (void *)arg, sizeof(count)))
			return -EFAULT;
              	/* printk("bulknotify: nfds %d\n", count); */
	      	for (i = 1; i <= count; i++, i++) {
	      		if (copy_from_user(&fd, &(((int *)arg)[i]), sizeof(fd)))
				return -EFAULT;
	      		if (copy_from_user(&nsent, &(((int *)arg)[i+1]), sizeof(nsent)))
				return -EFAULT;
                	/* printk("bulknotify: pid %d fd %d %d\n",
				current->pid, fd, nsent); */
			lock_kernel();
			sock = sockfd_lookup(fd, &err);
			if (!sock) goto nr_done;
			lsk = sock->sk;
			cm_notify(SK_CM_FLOW(lsk), nsent);
		sockfd_put(sock);
nr_done:
		unlock_kernel();
		}
	      return 0;
	}
	case SIOCCMUPDATE: {
		cm_update_t cmupdate;
		cmupdate.nrecd = 500;
		cmupdate.nloss = 1;
		cmupdate.lossmode = CM_TRANSIENT;
		cmupdate.rtt = 100; /* milliseconds! */
		/* printk("SIOCCMUPDATE called\n"); */
		if (copy_from_user(&cmupdate, (void *)arg,
				   sizeof(cm_update_t)))
			return -EFAULT;
		/* printk("UPDATE: nrecd %d nloss %d lossmode %d rtt %d\n",
		       cmupdate.nrecd, cmupdate.nloss,
		       cmupdate.lossmode, cmupdate.rtt); */
		/*cm_update(SK_CM_FLOW(sk), cmupdate.nrecd, cmupdate.nloss,
			  cmupdate.lossmode, cmupdate.rtt);*/
		cm_update(SK_CM_FLOW(sk), cmupdate.nrecd+cmupdate.nloss,
			cmupdate.nrecd, cmupdate.lossmode, cmupdate.rtt);
		return 0;
	}
	case SIOCCMBULKUPDATE: {
	      	int count;
	      	int fd;
	      	int i;
	      	int err;
		cm_update_t cmupdate;
	      	struct socket *sock;
	      	struct sock *lsk;
	      	if (copy_from_user(&count, (void *)arg, sizeof(count)))
			return -EFAULT;
              	/* printk("bulkupdate: nfds %d\n", count); */
	      	for (i = 1; i <= count; i++, i++) {
	      		if (copy_from_user(&fd, &(((int *)arg)[i]), sizeof(fd)))
				return -EFAULT;
	      		if (copy_from_user(&cmupdate, &(((int *)arg)[i+1]),
							sizeof(cm_update_t)))
				return -EFAULT;
			/* printk("bulkupdate: fd %d nrecd %d nloss %d lossmode %d rtt %d\n",
		       		fd, cmupdate.nrecd, cmupdate.nloss, 
		       		cmupdate.lossmode, cmupdate.rtt); */
			lock_kernel();
			sock = sockfd_lookup(fd, &err);
			if (!sock) goto up_done;
			lsk = sock->sk;
			cm_update(SK_CM_FLOW(lsk), cmupdate.nrecd+cmupdate.nloss,
			      cmupdate.nrecd, cmupdate.lossmode, cmupdate.rtt);
			sockfd_put(sock);
up_done:
			unlock_kernel();
		}
	      return 0;
	}
	case SIOCCMREADY: {
		int ready = cm_ready(SK_CM_FLOW(sk));
		if (copy_to_user((void *)arg, &ready, sizeof(int)))
			return -EFAULT;
		return 0;
		}
	case SIOCCMQUERY: {
		struct cm_query cmquery;
#ifdef CMDEMO
		int rndm2 = cm_get_time_rdtsc();
		cmquery.rate = rndm2 & 0x3;
		cmquery.srtt = 22.3;
#else
		cm_query(SK_CM_FLOW(sk), &cmquery);
#endif
		if (copy_to_user((void *)arg, &cmquery,
				sizeof(struct cm_query)))
			return -EFAULT;
		return 0;
		}
	case SIOCCMBULKQUERY: {
	      	int count = 0;
	      	int rcount = 0;
		int i;
		cm_flow_t *cflow = SK_CM_FLOW(sk);
		cm_flow_t *flow = cflow->bulknext;
		struct cm_fdquery cmfdquery;
              	/* printk("entering bulkquery %d\n", sizeof(cmfdquery)); */
	      	count = SK_CM_FLOW(sk)->bulkcount;
		for (i = 0; i < count; i++) {
			if (flow->pending == CM_REQUEST_GRANTED
				 || flow->ratechange) {
			   cmfdquery.fd = flow->fd;
			   cm_query(flow, &cmfdquery.cmquery);
			   cmfdquery.ready = 0;
			   if (flow->pending == CM_REQUEST_GRANTED) {
				cmfdquery.ready = 1;
			   	flow->pending = CM_REQUEST_INACTIVE;
			   }
			   rcount++;
			   if (copy_to_user(&(((struct cm_fdquery *)arg)[i]), &cmfdquery,
				sizeof(struct cm_fdquery)))
			   return -EFAULT;
			   }
			flow = flow->bulknext;
			}
              return rcount;
		}
	/*
	case SIOCCMREGISTER: {
		printk("cm_register return \n");
		return 0;
		}
	*/
	case SIOCCMCONGPARAM: {
		float k;
		if (copy_from_user(&k, (void *)arg, sizeof(float)))
			return -EFAULT;
		/* printk("NOTIFY: %d\n", nsent); */
#ifdef CONFIG_CM_BINOMIAL
		cm_set_cong_param(SK_CM_FLOW(sk), k);
		return 0;
#else
		return -EFAULT;
		/* lack of better fault */
#endif
	}
	case SIOCCMSETCONGALG: {
		int type;
		printk("setcongalg called\n");
		if (copy_from_user(&type, (void *)arg, sizeof(int)))
			return -EFAULT;		
		printk("setting alg to %d\n",type);
#ifdef CONFIG_CM_BINOMIAL
		printk("setting cong algo to %d\n",type);
		cm_mflow_setcongalg(SK_CM_MFLOW(sk),type);
		return 0;
#else
		return -EFAULT;   
		/* lack of better fault */
#endif
	}
        case SIOCCMGETMFLOW: {
		int mflowid = SK_CM_MFLOW(sk)->id;
		if (copy_to_user((void *)arg, &mflowid, sizeof(int)))
			return -EFAULT;
		return 0;
        }
        case SIOCCMSETMFLOW: {
		int err;
		cm_flow_t *flow;
		cm_macroflow_t *new_mflowid;
		if (copy_from_user(&new_mflowid, (void *)arg,
				   sizeof(cm_macroflow_t*)))
			return -EFAULT;
		flow = SK_CM_FLOW(sk);
		if (new_mflowid == (cm_macroflow_t *)-1) {
			/* need to delete first as cm_make_mflow
			   mutates mflow field in flow.  the
			   down side is, if cm_make_mflow fails,
			   the flow is not really attached to any mflow */
	      		cm_del_flow(SK_CM_MFLOW(sk), flow);
			err = cm_make_mflow(flow);
			if (err == -1) return -ENOBUFS;
			cm_add_flow(SK_CM_MFLOW(sk), flow);
			return 0;
		}
		if (cm_mflow_exists(new_mflowid) == 0) {
			cm_flow_t *flow = SK_CM_FLOW(sk);
			cm_del_flow(SK_CM_MFLOW(sk), flow);
			cm_add_flow(new_mflowid, flow);
			return 0;
		}
		return -EINVAL;
        }
        case SIOCCMGETTHRESHM: {
		cm_flow_t *flow = SK_CM_FLOW(sk);
		cmthresh thresh;
		thresh.downthreshmult = flow->downthreshmult;
		thresh.upthreshmult = flow->upthreshmult;
		if (copy_to_user((void *)arg, &thresh, sizeof(cmthresh)))
			return -EFAULT;
		return 0;
        }
        case SIOCCMSETTHRESHM: {
		cm_flow_t *flow = SK_CM_FLOW(sk);
		cmthresh thresh;
		if (copy_from_user(&thresh, (void *)arg,
				   sizeof(cmthresh)))
			return -EFAULT;
		if (thresh.downthreshmult < 0.0 ||
		    thresh.upthreshmult < 0.0) return -EINVAL;
		flow->downthreshmult = thresh.downthreshmult;
		flow->upthreshmult = thresh.upthreshmult;
		return 0;
	}
        case SIOCCMGETASSOC: {
	      	int count = 0;
	      	int fd;
		int i;
		cm_flow_t *cflow = SK_CM_FLOW(sk);
		cm_flow_t *flow = cflow->bulknext;
              	/* printk("entering getassoc\n"); */
	      	count = SK_CM_FLOW(sk)->bulkcount;
		for (i = 0; i < count; i++) {
			fd = flow->fd;
              		if (copy_to_user(&(((int *)arg)[i]), &fd, sizeof(fd)))
                      		return -EFAULT;
			flow = flow->bulknext;
		}
		return count;
        }
	case SIOCCMDELASSOC: {
		int count;
		cm_flow_t *ithflow;
		int found = 0;
		int i;
		int fd;
	      	int err;
	      	struct socket *sock;
	      	struct sock *lsk;
	      	if (copy_from_user(&count, (void *)arg, sizeof(count)))
			return -EFAULT;
              	/* printk("delassoc: nfds %d\n", count); */
	      	for (i = 1; i <= count; i++) {
	      		if (copy_from_user(&fd, &(((int *)arg)[i]), sizeof(fd)))
				return -EFAULT;
                	/* printk("delassoc: pid %d fd %d\n", current->pid, fd); */
			lock_kernel();
			sock = sockfd_lookup(fd, &err);
			if (!sock) goto da_done;
			lsk = sock->sk;
			ithflow = SK_CM_FLOW(sk)->bulknext;
			while (ithflow) {
				if (ithflow->sk == lsk) {
					ithflow->bulkprev->bulknext =
						ithflow->bulknext;
					ithflow->bulknext->bulkprev =
						ithflow->bulkprev;
					found = 1;
					break;
				}
			}
			if (found) SK_CM_FLOW(sk)->bulkcount--;
			/* else printk("delassoc: not found\n"); */
			sockfd_put(sock);
		da_done:
			unlock_kernel();
		}
		return 0;
	}
        case SIOCCMSETASSOC: {
	      	int count;
	      	int fd;
	      	int i;
	      	int err;
	      	struct socket *sock;
	      	struct sock *lsk;
              	/* printk("entering setassoc\n"); */
		if (SK_CM_FLOW(sk) == NULL) err = cm_open(sk);
		if (err != 0) return -ENOBUFS;
	      	if (copy_from_user(&count, (void *)arg, sizeof(count)))
			return -EFAULT;
              	/* printk("setassoc: nfds %d\n", count); */
	      	for (i = 1; i <= count; i++) {
	      		if (copy_from_user(&fd, &(((int *)arg)[i]), sizeof(fd)))
				return -EFAULT;
                	/* printk("setassoc: pid %d fd %d\n", current->pid, fd); */
			lock_kernel();
			sock = sockfd_lookup(fd, &err);
			if (!sock) goto sa_done;
			lsk = sock->sk;
			/* set up flow structure */
			err = cm_open(lsk);
			if (err != 0) return -ENOBUFS;
			/* put cmid into flow structure */
			lsk->tp_cm4->peersk = sk;
			/* put pid and fd into flow structure */
			lsk->tp_cm4->fd = fd;
			lsk->tp_cm4->pid = current->pid;
			SK_CM_FLOW(sk)->bulkcount++;
			if (SK_CM_FLOW(sk)->bulknext) {
				SK_CM_FLOW(sk)->bulknext->prev = SK_CM_FLOW(lsk);
				SK_CM_FLOW(lsk)->bulknext = SK_CM_FLOW(sk)->bulknext;
				SK_CM_FLOW(sk)->bulknext = SK_CM_FLOW(lsk);
			} else {
				SK_CM_FLOW(sk)->bulknext = SK_CM_FLOW(lsk);
				SK_CM_FLOW(lsk)->bulkprev = SK_CM_FLOW(sk);
			}
			sockfd_put(sock);
		sa_done:
			unlock_kernel();
		}
		return 0;
	}
	default:
		return -ENOIOCTLCMD;
	}
}

/*
 *	This should be easy, if there is something there
 *	we return it, otherwise we block.
 */

int cm_recvmsg(struct sock *sk, struct msghdr *msg, int len,
	       int noblock, int flags,int *addr_len)
{
	int copied=0;
	struct sk_buff *skb;
	int err;
	struct sockaddr_in *sin=(struct sockaddr_in *)msg->msg_name;
	
	if (flags & MSG_OOB)
		return -EOPNOTSUPP;
	
	if (addr_len)
		*addr_len=sizeof(*sin);
	
	if (flags & MSG_ERRQUEUE)
		return ip_recv_error(sk, msg, len);
	
	skb=skb_recv_datagram(sk,flags,noblock,&err);
	if(skb==NULL)
 		return err;

	copied = skb->len;
	if (len < copied)
		{
			msg->msg_flags |= MSG_TRUNC;
			copied = len;
		}
	
	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
	if (err)
		goto done;
	
	sk->stamp=skb->stamp;
	
	/* Copy the address. */
	if (sin) {
		sin->sin_family = AF_INET;
		sin->sin_addr.s_addr = skb->nh.iph->saddr;
	}
	if (sk->ip_cmsg_flags)
		ip_cmsg_recv(msg, skb);
 done:
	skb_free_datagram(sk, skb);
	return (err ? : copied);
}

static int cm_init(struct sock *sk)
{
	
	/* printk("Entering cm_init\n"); */
	return 0;
}

static int cm_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
	int err;
	/* struct sockaddr_in *usin = (struct sockaddr_in *) uaddr; */
	/* printk("cm_connect:control sk addr= %d port %d\n",usin->sin_addr.s_addr,usin->sin_port); */
	err = udp_connect(sk, uaddr, addr_len);
	if (err < 0) return err;
	err = cm_open(sk);
	if (err != 0) return -ENOBUFS;
	return 0;
}

static int cm_setsockopt(struct sock *sk, int level, int optname, 
			 char *optval, int optlen)
{
	int val = 0;
        struct sock *cm_controlsk;
	/* printk("Entering cm_setsockopt\n"); */

        if(optlen>=sizeof(int)) {
                if(get_user(val, (int *) optval))
                        return -EFAULT;
        } else if(optlen>=sizeof(char)) {
                unsigned char ucval;
                if(get_user(ucval, (unsigned char *) optval))
                        return -EFAULT;
                val = (int)ucval;
        }
        /* If optlen==0, it is equivalent to val == 0 */
	
	switch (optname) {
                case CM_OPTIONS:
                        CM_DPRINTF(CM_DEBUG_SOCKET, "cm_debug:  cm_setsockopt val %d called on %p\n", val, sk);
                        /* set up ptr to control skt here and also reverse ptr*/
                        cm_controlsk=cm_find_sk(sk);
                        if (cm_controlsk==NULL) {
                                CM_DPRINTF(CM_DEBUG_SOCKET|CM_DEBUG_ERRS,
                                           "cm_debug:  setsockopt:  can't find self\n");
                                return -EINVAL;
                        }
                        if (SK_CM_FLOW(cm_controlsk) == NULL) {
                                CM_DPRINTF(CM_DEBUG_SOCKET|CM_DEBUG_ERRS,
                                           "cm_debug:  setsockopt:  not whole\n");
                                return -EINVAL;
                        }
                        else {
                                SK_CM_FLOW(cm_controlsk)->peersk=sk;
                                SK_CM_FLOW(cm_controlsk)->cmapp_send = NULL;
                                if (val==CM_BUF) return -EINVAL;
                                if (val==CM_ALF) {
                                        SK_CM_FLOW(cm_controlsk)->cmapp_send =
                                                cmapp_send_wakeup;
					/* printk("setting cmapp_send_wakeup\n"); */
				}
                        }
                        SK_CM_FLOW(sk) = SK_CM_FLOW(cm_controlsk);
                        SK_CM_FLOW(sk)->peersk=sk;
                        if ((val==CM_ALF)){
                                if (SK_CM_FLOW(sk) == NULL) return -EINVAL;
                                SK_CM_FLOW(sk)->type=val;
                        }
                        if ((val==CM_RATE_CALLBACK)||(val==CM_NO_RATE_CALLBACK)){
                                if (SK_CM_FLOW(sk) == NULL) return -EINVAL;
                                SK_CM_FLOW(sk)->do_callback=val;
                        }
			/* printk("leaving cm_setsockopt\n"); */
                        return 0;
	default:
		return(-ENOPROTOOPT);
	};

	return -ENOPROTOOPT;
}

static int cm_getsockopt(struct sock *sk, int level, int optname, 
			 char *optval, int *optlen)
{
	/* printk("Entering cm_getsockopt\n"); */
	switch (optname) {
	};
	
	return -ENOPROTOOPT;
}

/*
 * Based on sock_no_fcntl.
 */
int cm_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
        struct sock *sk = sock->sk;
	
	/* printk("Entering cm_fcntl\n"); */
        switch(cmd)
		{
                case F_SETOWN:
                        /*
                         * This is a little restrictive, but it's the only
                         * way to make sure that you can't send a sigurg to
                         * another process.
                         */
                        if (current->pgrp != -arg &&
			    current->pid != arg &&
			    !capable(CAP_KILL)) return(-EPERM);
                        sk->proc = arg;
                        return(0);
                case F_GETOWN:
                        return(sk->proc);
		case F_SETFL:
                default:
                        return(-EINVAL);
		}
}

/*
 * Based on datagram_poll.
 */
unsigned int cm_poll(struct file * file, struct socket *sock, poll_table *wait)
{
        struct sock *sk = sock->sk;
        unsigned int mask;
	
        poll_wait(file, sk->sleep, wait);
        mask = 0;
	
        /* exceptional events? */ /* XXX seems optional */
        if (sk->err || !skb_queue_empty(&sk->error_queue))
                mask |= POLLERR;
        if (sk->shutdown & RCV_SHUTDOWN)
                mask |= POLLHUP;
	
        /* readable? */ /* XXX maybe set this when rate changes!!! */
	/* original code:
	   if (!skb_queue_empty(&sk->receive_queue))
	   mask |= POLLIN | POLLRDNORM;
	*/
	
        /* writable? */
	
	if (cm_ready(SK_CM_FLOW(sk))) 
		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
	else
		/* XXX not sure if this line is necessary... */
		sk->socket->flags |= SO_NOSPACE;
	/* appropriate when wock_wake_async is called with 2 */
	
	/* rate change? */
	/* printk("calling ratechange\n"); */
	if (cm_ratechange(SK_CM_FLOW(sk)))
		mask |= POLLPRI;

	/* printk("leaving poll\n"); */
        return mask;
}

void cmapp_send_wakeup(struct cm_flow *flow)
{
      struct sock *sk = flow->sk;
      /* struct socket *sock = sk->socket; */

      if (sk->dead) return;
      /* printk("entering cmapp_send %p %p\n", sock, sk); */
      /* printk("   %p\n", sk->sleep); */
      wake_up_interruptible(sk->sleep);
      sock_wake_async(sk->socket, 0);
      /* printk("leaving cmapp_send\n"); */
}


/* XXX ? values */
struct proto cm_prot = {
	(struct sock *)&cm_prot,	/* sklist_next */
	(struct sock *)&cm_prot,	/* sklist_prev */
	cm_closer,			/* close */
	cm_connect,			/* connect */
	NULL,				/* accept */
	NULL,				/* retransmit */
	NULL,				/* write_wakeup */
	NULL,				/* read_wakeup */
	cm_poll,			/* poll */
	cm_ioctl,			/* ioctl */
	cm_init,			/* init */
	NULL,				/* destroy */
	NULL,				/* shutdown */
	cm_setsockopt,			/* setsockopt */
	cm_getsockopt,			/* getsockopt */
	cm_sendmsg,			/* sendmsg */
	cm_recvmsg,			/* recvmsg */
	cm_bind,			/* bind */
	cm_rcv_skb,			/* backlog_rcv */
	cm_v4_hash,			/* hash */
	cm_v4_unhash,			/* unhash */
	cm_v4_rehash,			/* rehash */
	NULL,				/* good_socknum */
	NULL,				/* verify_bind */
	128,				/* max_header */  /* XXX ? value */
	0,				/* retransmits */
	"CM",				/* name */
	0,				/* inuse */
	0				/* highestinuse */
};
